﻿/*
 * Copyright (c) 1995 The Regents of the University of California.
 * All rights reserved.    
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice and the following
 * two paragraphs appear in all copies of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.    THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */

/*
 * Portions of this software Copyright (c) 1995 Brown University.
 * All rights reserved.
 * 
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement
 * is hereby granted, provided that the above copyright notice and the
 * following two paragraphs appear in all copies of this software.
 * 
 * IN NO EVENT SHALL BROWN UNIVERSITY BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF BROWN
 * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * BROWN UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE.    THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
 * BASIS, AND BROWN UNIVERSITY HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
 * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */

/*
Portions copyright (c) 2012 Eric Lasota

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define LOOSE_MPEG

using System;
using System.Collections.Generic;

using System.Text;


namespace MPEGnet
{
    public partial class VidStream
    {
        bool qualityFlag;

        void WriteDctRowToPlane( DCTROW row, byte[] px, int offset )
        {
            px[offset+0] = MPEGnet.video.BClamp(row.v0);
            px[offset+1] = MPEGnet.video.BClamp(row.v1);
            px[offset+2] = MPEGnet.video.BClamp(row.v2);
            px[offset+3] = MPEGnet.video.BClamp(row.v3);
            px[offset+4] = MPEGnet.video.BClamp(row.v4);
            px[offset+5] = MPEGnet.video.BClamp(row.v5);
            px[offset+6] = MPEGnet.video.BClamp(row.v6);
            px[offset+7] = MPEGnet.video.BClamp(row.v7);
        }
        
        void WriteDctBlockToPlane( DCTBLOCK block, byte[] px, int offset, int pitch )
        {
            WriteDctRowToPlane( block.r0, px, offset+0*pitch);
            WriteDctRowToPlane( block.r1, px, offset+1*pitch);
            WriteDctRowToPlane( block.r2, px, offset+2*pitch);
            WriteDctRowToPlane( block.r3, px, offset+3*pitch);
            WriteDctRowToPlane( block.r4, px, offset+4*pitch);
            WriteDctRowToPlane( block.r5, px, offset+5*pitch);
            WriteDctRowToPlane( block.r6, px, offset+6*pitch);
            WriteDctRowToPlane( block.r7, px, offset+7*pitch);
        }

        void AccumDctRowToPlane( ref DCTROW row, byte[] dest, int destOffset, byte[] src, int srcOffset )
        {
            dest[destOffset+0] = MPEGnet.video.BClamp(src[srcOffset+0] + row.v0);
            dest[destOffset+1] = MPEGnet.video.BClamp(src[srcOffset+1] + row.v1);
            dest[destOffset+2] = MPEGnet.video.BClamp(src[srcOffset+2] + row.v2);
            dest[destOffset+3] = MPEGnet.video.BClamp(src[srcOffset+3] + row.v3);
            dest[destOffset+4] = MPEGnet.video.BClamp(src[srcOffset+4] + row.v4);
            dest[destOffset+5] = MPEGnet.video.BClamp(src[srcOffset+5] + row.v5);
            dest[destOffset+6] = MPEGnet.video.BClamp(src[srcOffset+6] + row.v6);
            dest[destOffset+7] = MPEGnet.video.BClamp(src[srcOffset+7] + row.v7);
        }
        
        void AccumDctBlockToPlane( ref DCTBLOCK block, byte[] dest, int destOffset, byte[] src, int srcOffset, int pitch )
        {
            AccumDctRowToPlane(ref block.r0, dest, destOffset, src, srcOffset); srcOffset += pitch; destOffset += pitch;
            AccumDctRowToPlane(ref block.r1, dest, destOffset, src, srcOffset); srcOffset += pitch; destOffset += pitch;
            AccumDctRowToPlane(ref block.r2, dest, destOffset, src, srcOffset); srcOffset += pitch; destOffset += pitch;
            AccumDctRowToPlane(ref block.r3, dest, destOffset, src, srcOffset); srcOffset += pitch; destOffset += pitch;
            AccumDctRowToPlane(ref block.r4, dest, destOffset, src, srcOffset); srcOffset += pitch; destOffset += pitch;
            AccumDctRowToPlane(ref block.r5, dest, destOffset, src, srcOffset); srcOffset += pitch; destOffset += pitch;
            AccumDctRowToPlane(ref block.r6, dest, destOffset, src, srcOffset); srcOffset += pitch; destOffset += pitch;
            AccumDctRowToPlane(ref block.r7, dest, destOffset, src, srcOffset);
        }

        void ReconIMBlock( int bnum )
        {
            int mb_row, mb_col, row, col, row_size;
            byte[] destPlane;
            
            /* Calculate macroblock row and column from address. */

            mb_row = mblock.mb_address / (int)mb_width;
            mb_col = mblock.mb_address % (int)mb_width;

            /* If block is luminance block... */
            if (bnum < 4)
            {
                /* Calculate row and col values for upper left pixel of block. */
                row = mb_row * 16;
                col = mb_col * 16;
                if (bnum > 1)
                    row += 8;
                if (bnum % 2 != 0)
                    col += 8;

                /* Set dest to luminance plane of current pict image. */
                destPlane = current.luminance;

                /* Establish row size. */

                row_size = (int)(mb_width * 16);
            }
            /* Otherwise if block is Cr block... */
            /* Cr first because of the earlier mixup */
            else if (bnum == 5)
            {
                /* Set dest to Cr plane of current pict image. */

                destPlane = current.Cr;

                /* Establish row size. */
                row_size = (int)(mb_width * 8);

                /* Calculate row,col for upper left pixel of block. */
                row = mb_row * 8;
                col = mb_col * 8;
            }
            /* Otherwise block is Cb block, and ... */
            else
            {
                /* Set dest to Cb plane of current pict image. */
                destPlane = current.Cb;

                /* Establish row size. */

                row_size = (int)(mb_width * 8);

                /* Calculate row,col for upper left pixel value of block. */

                row = mb_row * 8;
                col = mb_col * 8;
            }

            /*
             * For each pixel in block, set to cropped reconstructed value from inverse
             * dct.
             */
            WriteDctBlockToPlane(block.dct_recon, destPlane, row * row_size + col, row_size);

        }


        /*
         *--------------------------------------------------------------
         *
         * ReconPMBlock --
         *
         *    Reconstructs forward predicted macroblocks.
         *
         * Results:
         *    None.
         *
         * Side effects:
         *    None.
         *
         *--------------------------------------------------------------
         */

        void ReconPMBlock(int bnum, int recon_right_for, int recon_down_for, int zflag)
        {
            int mb_row, mb_col, row, col, row_size;
            //unsigned char *rindex1, *rindex2, *rindex3, *rindex4;
            //unsigned char *index;
            //short int *blockvals;
            byte[] destPlane;
            byte[] pastPlane = null;

            #if LOOSE_MPEG
                int maxx, maxy;
                int illegalBlock = 0;
            #endif

            /* Calculate macroblock row and column from address. */
            mb_row = mblock.mb_address / (int)mb_width;
            mb_col = mblock.mb_address % (int)mb_width;

            if (bnum < 4)
            {
                /* Calculate right_for, down_for motion vectors. */

                right_for = recon_right_for >> 1;
                down_for = recon_down_for >> 1;
                right_half_for = recon_right_for & 0x1;
                down_half_for = recon_down_for & 0x1;

                /* Set dest to luminance plane of current pict image. */
                destPlane = current.luminance;
                if (picture.code_type == B_TYPE)
                {
                    if (past != null)
                        pastPlane = past.luminance;
                }
                else
                {
                    /* Set predictive frame to current future frame. */
                    if (future != null)
                        pastPlane = future.luminance;
                }

                /* Establish row size. */

                row_size = (int)(mb_width << 4);
                /* Calculate row,col of upper left pixel in block. */

                row = mb_row << 4;
                col = mb_col << 4;
                if (bnum > 1)
                    row += 8;
                if (bnum % 2 != 0)
                    col += 8;

                #if LOOSE_MPEG
                    /* Check for block illegality. */

                    maxx = (int)(mb_width*16-1);
                    maxy = (int)(mb_height*16-1);

                    if (row + down_for + down_half_for + 7 > maxy)
                        illegalBlock |= 0x4;
                    else if (row + down_for < 0)
                        illegalBlock |= 0x1;
    
                    if (col + right_for + right_half_for + 7 > maxx)
                        illegalBlock |= 0x2;
                    else if (col + right_for < 0)
                        illegalBlock |= 0x8;

                #endif
            }
            /* Otherwise, block is NOT luminance block, ... */
            else
            {
                /* Construct motion vectors. */
                recon_right_for /= 2;
                recon_down_for /= 2;
                right_for = recon_right_for >> 1;
                down_for = recon_down_for >> 1;
                right_half_for = recon_right_for & 0x1;
                down_half_for = recon_down_for & 0x1;

                /* Establish row size. */

                row_size = (int)(mb_width << 3);

                /* Calculate row,col of upper left pixel in block. */

                row = mb_row << 3;
                col = mb_col << 3;

                #if LOOSE_MPEG
                    /* Check for block illegality. */

                    maxx = (int)(mb_width*8-1);
                    maxy = (int)(mb_height*8-1);

                    if (row + down_for  + down_half_for  + 7 > maxy)
                        illegalBlock |= 0x4;
                    else if (row + down_for < 0)
                        illegalBlock |= 0x1;

                    if (col + right_for + right_half_for + 7 > maxx)
                        illegalBlock  |= 0x2;
                    else if (col + right_for < 0)
                        illegalBlock |= 0x8;

                #endif

                /* If block is Cr block... */
                /* 5 first because order was mixed up in earlier versions */

                if (bnum == 5)
                {

                    /* Set dest to Cr plane of current pict image. */

                    destPlane = current.Cr;

                    if (picture.code_type == B_TYPE)
                    {
                        if (past != null)
                            pastPlane = past.Cr;
                    }
                    else
                    {
                        if (future != null)
                            pastPlane = future.Cr;
                    }
                }
                /* Otherwise, block is Cb block... */
                else
                {
                    /* Set dest to Cb plane of current pict image. */
                    destPlane = current.Cb;
                    if (picture.code_type == B_TYPE)
                    {
                        if (past != null)
                            pastPlane = past.Cb;
                    }
                    else
                    {
                        if (future != null)
                            pastPlane = future.Cb;
                    }
                }
            }

            /* For each pixel in block... */

            #if LOOSE_MPEG
                if (illegalBlock == 0)
                {
            #endif

                    int destIndex = (row * row_size) + col;
                    int pastRIndex1 = (row + down_for) * row_size + col + right_for;

                    DCTBLOCK blockvals = block.dct_recon;
    
                    /*
                     * Calculate predictive pixel value based on motion vectors and copy to
                     * dest plane.
                     */
                    if ((down_half_for == 0) && (right_half_for == 0))
                    {
                        if (zflag == 0)
                            AccumDctBlockToPlane(ref block.dct_recon, destPlane, destIndex, pastPlane, pastRIndex1, row_size);
                        else
                        {
                            for (int rr = 0; rr < 8; rr++)
                            {
                                Array.Copy(pastPlane, pastRIndex1 , destPlane, destIndex, 8);
                                destIndex += row_size;
                                pastRIndex1 += row_size;
                            }
                        }
                    }
                    else
                    {
                        int pastRIndex2 = pastRIndex1 + right_half_for + (down_half_for * row_size);

                        /* if one of the two is zero, then quality makes no difference */
                        if ((right_half_for == 0) ||
                            (down_half_for == 0) || (!qualityFlag))
                        {
                            if (zflag == 0)
                            {
                                // index = crop((rindex1[0] + rindex2[0] + 1) >> 1) + blockvals[0]
                                Transforms.Transform_Avg2_AddDct_CAW(row_size, pastPlane, pastRIndex1, pastPlane, pastRIndex2, ref blockvals, destPlane, destIndex);
                            }
                            else
                            {
                                /* zflag */
                                Transforms.Transform_Avg2_Write(row_size, pastPlane, pastRIndex1, pastPlane, pastRIndex2, destPlane, destIndex);
                            }
                        }
                        else
                        {
                            /* qualityFlag on and both vectors are non-zero */
                            int pastRIndex3 = pastRIndex1 + right_half_for;
                            int pastRIndex4 = pastRIndex1 + (down_half_for * row_size);
                            if (zflag == 0)
                                Transforms.Transform_Avg4_AddDct_CAW(row_size, pastPlane, pastRIndex1, pastRIndex2, pastRIndex3, pastRIndex4, ref blockvals, destPlane, destIndex);
                            else
                            {
                                /* zflag */
                                Transforms.Transform_Avg4_Write(row_size, pastPlane, pastRIndex1, pastRIndex2, pastRIndex3, pastRIndex4, destPlane, destIndex);
                            }
                        }

                    }
            #if LOOSE_MPEG
                }
            #endif
        }


        /*
         *--------------------------------------------------------------
         *
         * ReconBMBlock --
         *
         *    Reconstructs back predicted macroblocks.
         *
         * Results:
         *    None.
         *
         * Side effects:
         *    None.
         *
         *--------------------------------------------------------------
         */

        void ReconBMBlock( int bnum, int recon_right_back, int recon_down_back, int zflag )
        {
            int mb_row, mb_col, row, col, row_size;
            int right_back, down_back, right_half_back, down_half_back;
            DCTBLOCK blockvals;
            byte[] destPlane = null;
            byte[] futurePlane = null;

            #if LOOSE_MPEG
                int illegalBlock = 0;
            #endif

            /* Calculate macroblock row and column from address. */
            mb_row = mblock.mb_address / (int)mb_width;
            mb_col = mblock.mb_address % (int)mb_width;

            /* If block is luminance block... */

            if (bnum < 4)
            {
                /* Calculate right_back, down_back motion vectors. */

                right_back = recon_right_back >> 1;
                down_back = recon_down_back >> 1;
                right_half_back = recon_right_back & 0x1;
                down_half_back = recon_down_back & 0x1;

                /* Set dest to luminance plane of current pict image. */

                destPlane = current.luminance;

                /*
                 * If future frame exists, set future to luminance plane of future frame.
                 */
                if (future != null)
                  futurePlane = future.luminance;

                /* Establish row size. */
                row_size = (int)(mb_width << 4);

                /* Calculate row,col of upper left pixel in block. */

                row = mb_row << 4;
                col = mb_col << 4;
                if (bnum > 1)
                  row += 8;
                if ((bnum % 2) != 0)
                  col += 8;

                #if LOOSE_MPEG
                    /* Check for block illegality. */

                    int maxx = (int)(mb_width*16-1);
                    int maxy = (int)(mb_height*16-1);

                    if (row + down_back + down_half_back + 7 > maxy)
                        illegalBlock |= 0x4;
                    else if (row + down_back < 0)
                        illegalBlock |= 0x1;
    
                    if (col + right_back + right_half_back + 7 > maxx)
                        illegalBlock |= 0x2;
                    else if (col + right_back < 0)
                        illegalBlock |= 0x8;

                #endif
            }
            /* Otherwise, block is NOT luminance block, ... */
            else
            {
                /* Construct motion vectors. */
                recon_right_back /= 2;
                recon_down_back /= 2;
                right_back = recon_right_back >> 1;
                down_back = recon_down_back >> 1;
                right_half_back = recon_right_back & 0x1;
                down_half_back = recon_down_back & 0x1;

                /* Establish row size. */

                row_size = (int)(mb_width << 3);

                /* Calculate row,col of upper left pixel in block. */

                row = mb_row << 3;
                col = mb_col << 3;

                #if LOOSE_MPEG

                    /* Check for block illegality. */

                    int maxx = (int)(mb_width*8-1);
                    int maxy = (int)(mb_height*8-1);

                    if (row + down_back + down_half_back + 7 > maxy)
                        illegalBlock |= 0x4;
                    else if (row + down_back < 0)
                        illegalBlock |= 0x1;

                    if (col + right_back + right_half_back + 7 > maxx)
                        illegalBlock  |= 0x2;
                    else if (col + right_back < 0)
                        illegalBlock |= 0x8;
                #endif

                /* If block is Cr block... */
                /* They were switched earlier, so 5 is first - eyhung */

                if (bnum == 5) 
                {
                    /* Set dest to Cr plane of current pict image. */
                    destPlane = current.Cr;

                    /*
                     * If future frame exists, set future to Cr plane of future image.
                     */

                    if (future != null)
                        futurePlane = future.Cr;
                }
                /* Otherwise, block is Cb block... */
                else
                {
                    /* Set dest to Cb plane of current pict image. */
                    destPlane = current.Cb;

                    /*
                     * If future frame exists, set future to Cb plane of future frame.
                     */
                    if (future != null)
                        futurePlane = future.Cb;
                }
            }

            /* For each pixel in block do... */

#if LOOSE_MPEG
            if (illegalBlock == 0)
            {
#endif

                int destIndex = (row * row_size) + col;
                int futureRIndex1 = (row + down_back) * row_size + col + right_back;

                blockvals = block.dct_recon;

                if ((right_half_back == 0) && (down_half_back == 0))
                {
                    if (zflag == 0)
                    {
                        Transforms.Transform_AddDct_CAW(row_size, futurePlane, futureRIndex1, ref blockvals, destPlane, destIndex);
                    }
                    else
                    {
                        for(int rr=0; rr<8; rr++)
                        {
                            Array.Copy(futurePlane, futureRIndex1, destPlane, destIndex, 8);
                                destIndex += row_size;
                                futureRIndex1 += row_size;
                        }
                    }
                }
                else
                {
                    int futureRIndex2 = futureRIndex1 + right_half_back + (down_half_back * row_size);
                    if (!qualityFlag)
                    {
                        if (zflag == 0)
                        {
                            Transforms.Transform_Avg2_AddDct_CAW(row_size, futurePlane, futureRIndex1, futurePlane, futureRIndex2, ref blockvals, destPlane, destIndex);
                        }
                        else
                        {
                            /* zflag */
                            Transforms.Transform_Avg2_Write(row_size, futurePlane, futureRIndex1, futurePlane, futureRIndex2, destPlane, destIndex);
                        }
                    }
                    else
                    {
                        /* qualityFlag on */
                        int futureRIndex3 = futureRIndex1 + right_half_back;
                        int futureRIndex4 = futureRIndex1 + (down_half_back * row_size);
                        if (zflag == 0)
                        {
                            Transforms.Transform_Avg4_AddDct_CAW(row_size, futurePlane, futureRIndex1, futureRIndex2, futureRIndex3, futureRIndex4, ref blockvals, destPlane, destIndex);
                        }
                        else
                        {
                            /* zflag */
                            Transforms.Transform_Avg4_Write(row_size, futurePlane, futureRIndex1, futureRIndex2, futureRIndex3, futureRIndex4, destPlane, destIndex);
                        }
                    }
                }
#if LOOSE_MPEG
            }
#endif
        }


        /*
         *--------------------------------------------------------------
         *
         * ReconBiMBlock --
         *
         *    Reconstructs bidirectionally predicted macroblocks.
         *
         * Results:
         *    None.
         *
         * Side effects:
         *    None.
         *
         *--------------------------------------------------------------
         */

        void ReconBiMBlock( int bnum,
                int recon_right_for, int recon_down_for, int recon_right_back,
                int recon_down_back, int zflag )
        {
            int mb_row, mb_col, row, col, row_size;
            byte[] destPlane = null;
            byte[] pastPlane = null;
            byte[] futurePlane = null;

            int right_for, down_for, right_half_for, down_half_for;
            int right_back, down_back, right_half_back, down_half_back;
            DCTBLOCK blockvals;
            int forw_row_start, back_row_start, forw_col_start, back_col_start;

            #if LOOSE_MPEG
                int lmaxx = (int)(mb_width*16-1);
                int lmaxy = (int)(mb_height*16-1);
                int cmaxx = (int)(mb_width*8-1);
                int cmaxy = (int)(mb_height*8-1);
                int illegal_forw = 0;
                int illegal_back = 0;
            #endif

            /* Calculate macroblock row and column from address. */

            mb_row = mblock.mb_address / (int)(mb_width);
            mb_col = mblock.mb_address % (int)(mb_width);

            /* If block is luminance block... */
            if (bnum < 4)
            {

                /*
                 * Calculate right_for, down_for, right_half_for, down_half_for,
                 * right_back, down_bakc, right_half_back, and down_half_back, motion
                 * vectors.
                 */

                right_for = recon_right_for >> 1;
                down_for = recon_down_for >> 1;
                right_half_for = recon_right_for & 0x1;
                down_half_for = recon_down_for & 0x1;

                right_back = recon_right_back >> 1;
                down_back = recon_down_back >> 1;
                right_half_back = recon_right_back & 0x1;
                down_half_back = recon_down_back & 0x1;

                /* Set dest to luminance plane of current pict image. */

                destPlane = current.luminance;

                /* If past frame exists, set past to luminance plane of past frame. */
                if (past != null)
                    pastPlane = past.luminance;

                /*
                 * If future frame exists, set future to luminance plane of future frame.
                 */

                if (future != null)
                  futurePlane = future.luminance;

                /* Establish row size. */
                row_size = (int)(mb_width << 4);

                /* Calculate row,col of upper left pixel in block. */

                row = (mb_row << 4);
                col = (mb_col << 4);
                if (bnum > 1)
                    row += 8;
                if ((bnum & 0x01) != 0)
                    col += 8;

#if LOOSE_MPEG
                /* Check for illegal pred. blocks. */

                /* Illegal forward Y block, right component */
                if (col + right_for + right_half_for + 7 > lmaxx)
                {
                    if(col > lmaxx)                                  // fix mb column 
                        col = lmaxx & ~15; 
                    if(col + right_for + 7 > lmaxx)                  // fix full component
                        right_for = lmaxx - col - 7;
                    if(col + right_for + right_half_for + 7 > lmaxx) // fix half component
                        right_half_for = 0;
                }
                else if (col + right_for < 0)
                {
                    if(col < 0)                                      // fix mb column
                        col = 0;
                    if(col + right_for < 0)                          // fix full component
                        right_for = 0;
                }

                /* Illegal forward Y block, down component */
                if (row + down_for + down_half_for + 7 > lmaxy)
                {
                    if(row > lmaxy)                                  // fix mb row
                        row = lmaxy & ~15; 
                    if(row + down_for + 7 > lmaxy)                   // fix full component
                        down_for = lmaxy - row - 7;
                    if(row + down_for + down_half_for + 7 > lmaxy)   // fix half component
                        down_half_for = 0;
                }
                else if (row + down_for < 0)
                {
                    if(row < 0)                                      // fix mb row
                        row = 0;
                    if(row + down_for < 0)                           // fix full component
                        down_for = 0;
                }


                /* Illegal backward Y block, right component */
                if (col + right_back + right_half_back + 7 > lmaxx)
                {
                    if(col > lmaxx)                                    // fix mb column 
                        col = lmaxx & ~15; 
                    if(col + right_back + 7 > lmaxx)                   // fix full component
                        right_back = lmaxx - col - 7;
                    if(col + right_back + right_half_back + 7 > lmaxx) // fix half component
                        right_half_back = 0;
                }
                else if (col + right_back < 0)
                {
                    if(col < 0)                                        // fix mb column
                        col = 0;
                    if(col + right_back < 0)                           // fix full component
                        right_back = 0;
                }

                /* Illegal backward Y block, down component */
                if (row + down_back + down_half_back + 7 > lmaxy)
                {
                    if(row > lmaxy)                                    // fix mb row
                        row = lmaxy & ~15; 
                    if(row + down_back + 7 > lmaxy)                    // fix full component
                        down_back = lmaxy - row - 7;
                    if(row + down_back + down_half_back + 7 > lmaxy)   // fix half component
                        down_half_back = 0;
                }
                else if (row + down_back < 0)
                {
                    if(row < 0)                                        // fix mb row
                        row = 0;
                    if(row + down_back < 0)                            // fix full component
                        down_back = 0;
                }
#endif

                forw_col_start = col + right_for;
                forw_row_start = row + down_for;

                back_col_start = col + right_back;
                back_row_start = row + down_back;

            }
            /* Otherwise, block is NOT luminance block, ... */
            else
            {
                /* Construct motion vectors. */

                recon_right_for /= 2;
                recon_down_for /= 2;
                right_for = recon_right_for >> 1;
                down_for = recon_down_for >> 1;
                right_half_for = recon_right_for & 0x1;
                down_half_for = recon_down_for & 0x1;

                recon_right_back /= 2;
                recon_down_back /= 2;
                right_back = recon_right_back >> 1;
                down_back = recon_down_back >> 1;
                right_half_back = recon_right_back & 0x1;
                down_half_back = recon_down_back & 0x1;

                /* Establish row size. */
                row_size = (int)(mb_width << 3);

                /* Calculate row,col of upper left pixel in block. */

                row = (mb_row << 3);
                col = (mb_col << 3);

#if LOOSE_MPEG

                /* Check for illegal pred. blocks. */


                /* Illegal forward C block, right component */
                if (col + right_for + right_half_for + 7 > cmaxx)
                {
                    if(col > cmaxx)                                  // fix mb column 
                        col = cmaxx & ~15; 
                    if(col + right_for + 7 > cmaxx)                  // fix full component
                        right_for = cmaxx - col - 7;
                    if(col + right_for + right_half_for + 7 > cmaxx) // fix half component
                        right_half_for = 0;
                }
                else if (col + right_for < 0)
                {
                    if(col < 0)                                      // fix mb column
                        col = 0;
                    if(col + right_for < 0)                          // fix full component
                        right_for = 0;
                }
    
                /* Illegal forward C block, down component */
                if (row + down_for + down_half_for + 7 > cmaxy)
                {
                    if(row > cmaxy)                                  // fix mb row
                        row = cmaxy & ~15; 
                    if(row + down_for + 7 > cmaxy)                   // fix full component
                        down_for = cmaxy - row - 7;
                    if(row + down_for + down_half_for + 7 > cmaxy)   // fix half component
                        down_half_for = 0;
                }
                else if (row + down_for < 0)
                {
                    if(row < 0)                                      // fix mb row
                        row = 0;
                    if(row + down_for < 0)                           // fix full component
                        down_for = 0;
                }


                /* Illegal backward C block, right component */
                if (col + right_back + right_half_back + 7 > cmaxx)
                {
                    if(col > cmaxx)                                    // fix mb column 
                        col = cmaxx & ~15; 
                    if(col + right_back + 7 > cmaxx)                   // fix full component
                        right_back = cmaxx - col - 7;
                    if(col + right_back + right_half_back + 7 > cmaxx) // fix half component
                        right_half_back = 0;
                }
                else if (col + right_back < 0)
                {
                    if(col < 0)                                        // fix mb column
                        col = 0;
                    if(col + right_back < 0)                           // fix full component
                        right_back = 0;
                }

                /* Illegal backward C block, down component */
                if (row + down_back + down_half_back + 7 > cmaxy)
                {
                    if(row > cmaxy)                                    // fix mb row
                        row = cmaxy & ~15; 
                    if(row + down_back + 7 > cmaxy)                    // fix full component
                        down_back = cmaxy - row - 7;
                    if(row + down_back + down_half_back + 7 > cmaxy)   // fix half component
                        down_half_back = 0;
                }
                else if (row + down_back < 0)
                {
                    if(row < 0)                                        // fix mb row
                        row = 0;
                    if(row + down_back < 0)                            // fix full component
                        down_back = 0;
                }
#endif

                forw_col_start = col + right_for;
                forw_row_start = row + down_for;

                back_col_start = col + right_back;
                back_row_start = row + down_back;
    
                /* If block is Cr block... */
                /* Switched earlier, so we test Cr first - eyhung */

                if (bnum == 5)
                {
                    /* Set dest to Cr plane of current pict image. */
                    destPlane = current.Cr;

                    /* If past frame exists, set past to Cr plane of past image. */
                    if (past != null)
                        pastPlane = past.Cr;

                    /*
                     * If future frame exists, set future to Cr plane of future image.
                     */
                    if (future != null)
                        futurePlane = future.Cr;
                }
                /* Otherwise, block is Cb block... */
                else
                {
                    /* Set dest to Cb plane of current pict image. */
                    destPlane = current.Cb;

                    /* If past frame exists, set past to Cb plane of past frame. */

                    if (past != null)
                        pastPlane = past.Cb;

                    /*
                     * If future frame exists, set future to Cb plane of future frame.
                     */

                    if (future != null)
                        futurePlane = future.Cb;
                }
            }

            /* For each pixel in block... */

            int destIndex = (row * row_size) + col;

            ByteCursor rindex1;
            ByteCursor bindex1;
#if LOOSE_MPEG
            if (illegal_forw != 0) 
                rindex1 = new ByteCursor(futurePlane, back_row_start * row_size + back_col_start);
            else 
#endif
                rindex1 = new ByteCursor(pastPlane, forw_row_start  * row_size + forw_col_start);

#if LOOSE_MPEG
            if (illegal_back != 0) 
                bindex1 = new ByteCursor(pastPlane, forw_row_start * row_size + forw_col_start);
            else 
#endif
                bindex1 = new ByteCursor(futurePlane, back_row_start * row_size + back_col_start);

            blockvals = block.dct_recon;
            {
                if (zflag == 0)
                {
                    Transforms.Transform_Avg2_AddDct_CAW(row_size, rindex1.Array, rindex1.Address, bindex1.Array, bindex1.Address, ref blockvals, destPlane, destIndex);
                }
                else
                {
                    Transforms.Transform_Avg2_Write(row_size, rindex1.Array, rindex1.Address, bindex1.Array, bindex1.Address, destPlane, destIndex);
                }
            }
        }

        
        /*
         *--------------------------------------------------------------
         *
         * ReconSkippedBlock --
         *
         *    Reconstructs predictive block for skipped macroblocks
         *      in B Frames.
         *
         * Results:
         *    No return values.
         *
         * Side effects:
         *    None.
         *
         *--------------------------------------------------------------
         */

        static IntBlock ReconSkippedBlock8( byte[] sourcePlane,
          int row, int col, int row_size, int right, int down,
          int right_half, int down_half )
        {
            int sourceIndex = ((row + down) * row_size) + col + right;

            if ((right_half == 0) && (down_half == 0))
                return new IntBlock(sourcePlane, sourceIndex, row_size);
            else
            {
                int sourceIndex2 = sourceIndex + right_half + (row_size * down_half);
                IntBlock sblock = new IntBlock(sourcePlane, sourceIndex, row_size);
                IntBlock sblock2 = new IntBlock(sourcePlane, sourceIndex2, row_size);
                return sblock.Add(sblock2).RightShift(1);
            }
        }

        static IntBlock16 ReconSkippedBlock16(byte[] sourcePlane,
          int row, int col, int row_size, int right, int down,
          int right_half, int down_half)
        {
            int sourceIndex = ((row + down) * row_size) + col + right;

            if ((right_half == 0) && (down_half == 0))
                return new IntBlock16(sourcePlane, sourceIndex, row_size);
            else
            {
                int sourceIndex2 = sourceIndex + right_half + (row_size * down_half);

                IntBlock16 sblock = new IntBlock16(sourcePlane, sourceIndex, row_size);
                IntBlock16 sblock2 = new IntBlock16(sourcePlane, sourceIndex2, row_size);
                return sblock.Add(sblock2).RightShift(1);
            }
        }


        /*
         *--------------------------------------------------------------
         *
         * ProcessSkippedPFrameMBlocks --
         *
         *    Processes skipped macroblocks in P frames.
         *
         * Results:
         *    Calculates pixel values for luminance, Cr, and Cb planes
         *      in current pict image for skipped macroblocks.
         *
         * Side effects:
         *    Pixel values in pict image changed.
         *
         *--------------------------------------------------------------
         */

        void ProcessSkippedPFrameMBlocks( )
        {
            int row_size, half_row, mb_row, mb_col, row, col;
            int row_incr, half_row_incr, crow, ccol;

            /* Calculate row sizes for luminance and Cr/Cb macroblock areas. */

            row_size = (int)(mb_width << 4);
            half_row = (row_size >> 1);

            /* For each skipped macroblock, do... */

            for (int addr = mblock.past_mb_addr + 1;
               addr < mblock.mb_address; addr++)
            {
                _recon.Macroblocks[addr].skipped = true;

                /* Calculate macroblock row and col. */

                mb_row = addr / (int)(mb_width);
                mb_col = addr % (int)(mb_width);

                /* Calculate upper left pixel row,col for luminance plane. */

                row = mb_row << 4;
                col = mb_col << 4;

                /* For each row in macroblock luminance plane... */

                int currentDestIndex = (row * row_size) + col;
                int futureSrcIndex = (row * row_size) + col;

                for(int rr=0;rr<16;rr++)
                {
                    Array.Copy(future.luminance, futureSrcIndex, current.luminance, currentDestIndex, 16);
                    currentDestIndex += row_size;
                    futureSrcIndex += row_size;
                }

                /*
                 * Divide row,col to get upper left pixel of macroblock in Cr and Cb
                 * planes.
                 */

                crow = row >> 1;
                ccol = col >> 1;
                
                currentDestIndex = (crow * half_row) + ccol;
                futureSrcIndex = (crow * half_row) + ccol;

                /* For each row in Cr, and Cb planes... */
                for(int rr=0;rr<8;rr++)
                {
                    Array.Copy(future.Cb, futureSrcIndex, current.Cb, currentDestIndex, 8);
                    Array.Copy(future.Cr, futureSrcIndex, current.Cr, currentDestIndex, 8);
                    currentDestIndex += half_row;
                    futureSrcIndex += half_row;
                }
            }

            mblock.recon_right_for_prev = 0;
            mblock.recon_down_for_prev = 0;
        }


        /*
         *--------------------------------------------------------------
         *
         * ProcessSkippedBFrameMBlocks --
         *
         *    Processes skipped macroblocks in B frames.
         *
         * Results:
         *    Calculates pixel values for luminance, Cr, and Cb planes
         *      in current pict image for skipped macroblocks.
         *
         * Side effects:
         *    Pixel values in pict image changed.
         *
         *--------------------------------------------------------------
         */
        void ProcessSkippedBFrameMBlocks( )
        {
            int row_size, half_row, mb_row, mb_col, row, col;
            int right_half_for = 0, down_half_for = 0;
            int c_right_half_for = 0, c_down_half_for = 0;
            int right_half_back = 0, down_half_back = 0;
            int c_right_half_back = 0, c_down_half_back = 0;
            int right_for = 0, down_for = 0;
            int recon_right_for, recon_down_for;
            int recon_right_back, recon_down_back;
            int right_back = 0, down_back = 0;
            int c_right_for = 0, c_down_for = 0;
            int c_right_back = 0, c_down_back = 0;
            IntBlock16 forw_lum, back_lum;
            IntBlock forw_cr, forw_cb, back_cr, back_cb;
            int row_incr, half_row_incr;
            int ccol, crow;
#if LOOSE_MPEG
            int lmaxx = (int)(mb_width*16-1);
            int lmaxy = (int)(mb_height*16-1);
            int cmaxx = (int)(mb_width*8-1);
            int cmaxy = (int)(mb_height*8-1);
#endif

            /* Calculate row sizes for luminance and Cr/Cb macroblock areas. */

            row_size = (int)(mb_width << 4);
            half_row = (row_size >> 1);

            /* Establish motion vector codes based on full pixel flag. */

            if (picture.full_pel_forw_vector) {
                recon_right_for = mblock.recon_right_for_prev << 1;
                recon_down_for = mblock.recon_down_for_prev << 1;
            } else {
                recon_right_for = mblock.recon_right_for_prev;
                recon_down_for = mblock.recon_down_for_prev;
            }

            if (picture.full_pel_back_vector) {
                recon_right_back = mblock.recon_right_back_prev << 1;
                recon_down_back = mblock.recon_down_back_prev << 1;
            } else {
                recon_right_back = mblock.recon_right_back_prev;
                recon_down_back = mblock.recon_down_back_prev;
            }

            /* If only one motion vector, do display copy, else do full
               calculation. 
            */
            /* Calculate motion vectors. */
  
            if (mblock.bpict_past_forw)
            {
                right_for = recon_right_for >> 1;
                down_for = recon_down_for >> 1;
                right_half_for = recon_right_for & 0x1;
                down_half_for = recon_down_for & 0x1;
    
                recon_right_for /= 2;
                recon_down_for /= 2;
                c_right_for = recon_right_for >> 1;
                c_down_for = recon_down_for >> 1;
                c_right_half_for = recon_right_for & 0x1;
                c_down_half_for = recon_down_for & 0x1;
    
            }
            if (mblock.bpict_past_back)
            {
                right_back = recon_right_back >> 1;
                down_back = recon_down_back >> 1;
                right_half_back = recon_right_back & 0x1;
                down_half_back = recon_down_back & 0x1;
    
                recon_right_back /= 2;
                recon_down_back /= 2;
                c_right_back = recon_right_back >> 1;
                c_down_back = recon_down_back >> 1;
                c_right_half_back = recon_right_back & 0x1;
                c_down_half_back = recon_down_back & 0x1;
            }

            /* For each skipped macroblock, do... */
            for (int addr = mblock.past_mb_addr + 1;
               addr < mblock.mb_address; addr++)
            {
                _recon.Macroblocks[addr].skipped = true;
                /* Calculate macroblock row and col. */
    
                mb_row = addr / (int)(mb_width);
                mb_col = addr % (int)(mb_width);
    
                /* Calculate upper left pixel row,col for luminance plane. */
    
                row = mb_row << 4;
                col = mb_col << 4;
                crow = row / 2;
                ccol = col / 2;

#if LOOSE_MPEG

                /* Check for illegal blocks. */

                /* Illegal forward Y block, right component */
                if (col + right_for + right_half_for + 7 > lmaxx)
                {
                    if(col > lmaxx)                                  // fix mb column 
                        col = lmaxx & ~15; 
                    if(col + right_for + 7 > lmaxx)                  // fix full component
                        right_for = lmaxx - col - 7;
                    if(col + right_for + right_half_for + 7 > lmaxx) // fix half component
                        right_half_for = 0;
                }
                else if (col + right_for < 0)
                {
                    if(col < 0)                                      // fix mb column
                        col = 0;
                    if(col + right_for < 0)                          // fix full component
                        right_for = 0;
                }

                /* Illegal forward Y block, down component */
                if (row + down_for + down_half_for + 7 > lmaxy)
                {
                    if(row > lmaxy)                                  // fix mb row
                        row = lmaxy & ~15; 
                    if(row + down_for + 7 > lmaxy)                   // fix full component
                        down_for = lmaxy - row - 7;
                    if(row + down_for + down_half_for + 7 > lmaxy)   // fix half component
                        down_half_for = 0;
                }
                else if (row + down_for < 0)
                {
                    if(row < 0)                                      // fix mb row
                        row = 0;
                    if(row + down_for < 0)                           // fix full component
                        down_for = 0;
                }

                /* Illegal backward Y block, right component */
                if (col + right_back + right_half_back + 7 > lmaxx)
                {
                    if(col > lmaxx)                                    // fix mb column 
                        col = lmaxx & ~15; 
                    if(col + right_back + 7 > lmaxx)                   // fix full component
                        right_back = lmaxx - col - 7;
                    if(col + right_back + right_half_back + 7 > lmaxx) // fix half component
                        right_half_back = 0;
                }
                else if (col + right_back < 0)
                {
                    if(col < 0)                                        // fix mb column
                        col = 0;
                    if(col + right_back < 0)                           // fix full component
                        right_back = 0;
                }

                /* Illegal backward Y block, down component */
                if (row + down_back + down_half_back + 7 > lmaxy)
                {
                    if(row > lmaxy)                                    // fix mb row
                        row = lmaxy & ~15; 
                    if(row + down_back + 7 > lmaxy)                    // fix full component
                        down_back = lmaxy - row - 7;
                    if(row + down_back + down_half_back + 7 > lmaxy)   // fix half component
                        down_half_back = 0;
                }
                else if (row + down_back < 0)
                {
                    if(row < 0)                                        // fix mb row
                        row = 0;
                    if(row + down_back < 0)                            // fix full component
                        down_back = 0;
                }


                /* Illegal forward C block, right component */
                if (ccol + c_right_for + c_right_half_for + 7 > cmaxx)
                {
                    if(ccol > cmaxx)                                      // fix mb column 
                        ccol = cmaxx & ~15; 
                    if(ccol + c_right_for + 7 > cmaxx)                    // fix full component
                        c_right_for = cmaxx - ccol - 7;
                    if(ccol + c_right_for + c_right_half_for + 7 > cmaxx) // fix half component
                        c_right_half_for = 0;
                }
                else if (ccol + c_right_for < 0)
                {
                    if(ccol < 0)                                          // fix mb column
                        ccol = 0;
                    if(ccol + c_right_for < 0)                            // fix full component
                        c_right_for = 0;
                }

                /* Illegal forward C block, down component */
                if (crow + c_down_for + c_down_half_for + 7 > cmaxy)
                {
                    if(crow > cmaxy)                                      // fix mb row
                        crow = cmaxy & ~15; 
                    if(crow + c_down_for + 7 > cmaxy)                     // fix full component
                        c_down_for = cmaxy - crow - 7;
                    if(crow + c_down_for + c_down_half_for + 7 > cmaxy)   // fix half component
                        c_down_half_for = 0;
                }
                else if (crow + c_down_for < 0)
                {
                    if(crow < 0)                                           // fix mb row
                        crow = 0;
                    if(crow + c_down_for < 0)                              // fix full component
                        c_down_for = 0;
                }

                /* Illegal backward C block, right component */
                if (ccol + c_right_back + c_right_half_back + 7 > cmaxx)
                {
                    if(ccol > cmaxx)                                        // fix mb column 
                        ccol = cmaxx & ~15; 
                    if(ccol + c_right_back + 7 > cmaxx)                     // fix full component
                        c_right_back = cmaxx - ccol - 7;
                    if(ccol + c_right_back + c_right_half_back + 7 > cmaxx) // fix half component
                        c_right_half_back = 0;
                }
                else if (ccol + c_right_back < 0)
                {
                    if(ccol < 0)                                            // fix mb column
                        ccol = 0;
                    if(ccol + c_right_back < 0)                             // fix full component
                        c_right_back = 0;
                }

                /* Illegal backward C block, down component */
                if (crow + c_down_back + c_down_half_back + 7 > cmaxy)
                {
                    if(crow > cmaxy)                                        // fix mb row
                        crow = cmaxy & ~15; 
                    if(crow + c_down_back + 7 > cmaxy)                      // fix full component
                        c_down_back = cmaxy - crow - 7;
                    if(crow + c_down_back + c_down_half_back + 7 > cmaxy)   // fix half component
                        c_down_half_back = 0;
                }
                else if (crow + c_down_back < 0)
                {
                    if(crow < 0)                                            // fix mb row
                        crow = 0;
                    if(crow + c_down_back < 0)                              // fix full component
                        c_down_back = 0;
                }
#endif

                /* If forward predicted, calculate prediction values. */
                if (mblock.bpict_past_forw)
                {
                    forw_lum = ReconSkippedBlock16(past.luminance,
                                row, col, row_size, right_for, down_for,
                                right_half_for, down_half_for);
                    forw_cr = ReconSkippedBlock8(past.Cr, crow,
                                ccol, half_row,
                                c_right_for, c_down_for, c_right_half_for, c_down_half_for);
                    forw_cb = ReconSkippedBlock8(past.Cb, crow,
                                ccol, half_row,
                                c_right_for, c_down_for, c_right_half_for, c_down_half_for);

                    if (mblock.bpict_past_back)
                    {
                        back_lum = ReconSkippedBlock16(future.luminance,
                                    row, col, row_size, right_back, down_back,
                                    right_half_back, down_half_back);
                        back_cr = ReconSkippedBlock8(future.Cr, crow,
                                    ccol, half_row,
                                    c_right_back, c_down_back,
                                    c_right_half_back, c_down_half_back);
                        back_cb = ReconSkippedBlock8(future.Cb, crow,
                                    ccol, half_row,
                                    c_right_back, c_down_back,
                                    c_right_half_back, c_down_half_back);

                        forw_lum.AverageWrite(ref back_lum, current.luminance, (row * row_size) + col, row_size);
                        forw_cr.AverageWrite(ref back_cr, current.Cr, (crow * half_row) + ccol, half_row);
                        forw_cb.AverageWrite(ref back_cb, current.Cb, (crow * half_row) + ccol, half_row);
                    }
                    else
                    {
                        forw_lum.SimpleWrite(current.luminance, (row * row_size) + col, row_size);
                        forw_cr.SimpleWrite(current.Cr, (crow * half_row) + ccol, half_row);
                        forw_cb.SimpleWrite(current.Cb, (crow * half_row) + ccol, half_row);
                    }

                }
                /* If back predicted, calculate prediction values. */
                else if (mblock.bpict_past_back)
                {
                    back_lum = ReconSkippedBlock16(future.luminance,
                                row, col, row_size, right_back, down_back,
                                right_half_back, down_half_back);
                    back_cr = ReconSkippedBlock8(future.Cr, crow,
                                ccol, half_row,
                                c_right_back, c_down_back,
                                c_right_half_back, c_down_half_back);
                    back_cb = ReconSkippedBlock8(future.Cb, crow,
                                ccol, half_row,
                                c_right_back, c_down_back,
                                c_right_half_back, c_down_half_back);

                    back_lum.SimpleWrite(current.luminance, (row * row_size) + col, row_size);
                    back_cr.SimpleWrite(current.Cr, (crow * half_row) + ccol, half_row);
                    back_cb.SimpleWrite(current.Cb, (crow * half_row) + ccol, half_row);
                }
            }
        }


        
        /*
         *--------------------------------------------------------------
         *
         * ParseMacroBlock --
         *
         *      Parseoff macroblock. Reconstructs DCT values. Applies
         *      inverse DCT, reconstructs motion vectors, calculates and
         *      set pixel values for macroblock in current pict image
         *      structure.
         *
         * Results:
         *      Here's where everything really happens. Welcome to the
         *      heart of darkness.
         *
         * Side effects:
         *      Bit stream irreversibly parsed off.
         *
         *--------------------------------------------------------------
         */
        int ParseMacroBlock( )
        {
            int addr_incr;
            uint data;
            int recon_right_for = 0, recon_down_for = 0, recon_right_back = 0,
                  recon_down_back = 0;
            int zero_block_flag;
            MBQuantType mb_quant = MBQuantType.False;
            bool mb_motion_forw = false, mb_motion_back = false, 
                  mb_pattern = false;
            
            /*
             * Parse off macroblock address increment and add to macroblock address.
             */
            uint mbSafety = 1986;                /* 256*256/33, the maximum possible number of mb incs */
            do
            {
                uint ind;

                ind = show_bits11();
                addr_incr = DecodeMBAddrInc();

                if (VlcTables.mb_addr_inc[ind].num_bits == 0)
                    addr_incr = 1;
                if (addr_incr == MB_ESCAPE)
                {
                    mblock.mb_address += 33;
                    addr_incr = MB_STUFFING;
                }
                mbSafety--;
            } while (addr_incr == MB_STUFFING && mbSafety != 0);

            mblock.mb_address += addr_incr;

            if( mblock.mb_address > (int) (mb_height * mb_width - 1) )
                return SKIP_TO_START_CODE;
            if( mblock.mb_address < 0 )
                return SKIP_TO_START_CODE;

            /*
             * If macroblocks have been skipped, process skipped macroblocks.
             */

            if (mblock.mb_address - mblock.past_mb_addr > 1)
            {
                /* Use current mblock prediction info */
                MBlockInfo mbi = new MBlockInfo(ref mblock, true);

                /* Copy this to all recons */
                for (int i = mblock.past_mb_addr + 1; i < mblock.mb_address; i++)
                    _recon.Macroblocks[i] = mbi;

                if (picture.code_type == P_TYPE)
                {
                    // Reset the predictor
                    mblock.recon_right_for_prev = 0;
                    mblock.recon_down_for_prev = 0;
                }
            }
            /* Set past macroblock address to current macroblock address. */
            mblock.past_mb_addr = mblock.mb_address;

            /* Based on picture type decode macroblock type. */
            switch (picture.code_type)
            {
            case I_TYPE:
                DecodeMBTypeI(out mb_quant, out mb_motion_forw, out mb_motion_back, out mb_pattern,
                          out mblock.mb_intra);
                break;

            case P_TYPE:
                DecodeMBTypeP(out mb_quant, out mb_motion_forw, out mb_motion_back, out mb_pattern,
                          out mblock.mb_intra);
                break;

            case B_TYPE:
                DecodeMBTypeB(out mb_quant, out mb_motion_forw, out mb_motion_back, out mb_pattern,
                          out mblock.mb_intra);
                break;
            case D_TYPE:
                _smpeg.SetError("D-frames not supported");
                return SKIP_TO_START_CODE;
            }

            /* If quantization flag set, parse off new quantization scale. */

            if (mb_quant == MBQuantType.True)
            {
                data = get_bits5();
                slice.quant_scale = data;
            }
            /* If forward motion vectors exist... */
            if (mb_motion_forw)
            {
                /* Parse off and decode horizontal forward motion vector. */
                mblock.motion_h_forw_code = DecodeMotionVectors();

                /* If horiz. forward r data exists, parse off. */

                if ((picture.forw_f != 1) &&
                    (mblock.motion_h_forw_code != 0))
                {
                    data = get_bitsn((int)picture.forw_r_size);
                    mblock.motion_h_forw_r = data;
                }
                /* Parse off and decode vertical forward motion vector. */
                mblock.motion_v_forw_code = DecodeMotionVectors();

                /* If vert. forw. r data exists, parse off. */

                if ((picture.forw_f != 1) &&
                    (mblock.motion_v_forw_code != 0))
                {
                    data = get_bitsn((int)picture.forw_r_size);
                    mblock.motion_v_forw_r = data;
                }
            }
            /* If back motion vectors exist... */
            if (mb_motion_back)
            {
                /* Parse off and decode horiz. back motion vector. */
                mblock.motion_h_back_code = DecodeMotionVectors();

                /* If horiz. back r data exists, parse off. */

                if ((picture.back_f != 1) &&
                    (mblock.motion_h_back_code != 0))
                {
                    data = get_bitsn((int)picture.back_r_size);
                    mblock.motion_h_back_r = data;
                }
                /* Parse off and decode vert. back motion vector. */
                mblock.motion_v_back_code = DecodeMotionVectors();

                /* If vert. back r data exists, parse off. */

                if ((picture.back_f != 1) &&
                    (mblock.motion_v_back_code != 0))
                {
                    data = get_bitsn((int)picture.back_r_size);
                    mblock.motion_v_back_r = data;
                }
            }

            /* If mblock pattern flag set, parse and decode CBP (code block pattern). */
            if (mb_pattern)
                mblock.cbp = DecodeCBP();
            /* Otherwise, set CBP to zero. */
            else
                mblock.cbp = 0;

            /* Reconstruct motion vectors depending on picture type. */
            if (picture.code_type == P_TYPE)
            {
                /*
                 * If no forw motion vectors, reset previous and current vectors to 0.
                 */

                if (!mb_motion_forw)
                {
                    recon_right_for = 0;
                    recon_down_for = 0;
                    mblock.recon_right_for_prev = 0;
                    mblock.recon_down_for_prev = 0;
                }
                /*
                 * Otherwise, compute new forw motion vectors. Reset previous vectors to
                 * current vectors.
                 */
                else
                    ComputeForwVector(ref recon_right_for, ref recon_down_for);
            }
            if (picture.code_type == B_TYPE)
            {
                /* Reset prev. and current vectors to zero if mblock is intracoded. */

                if (mblock.mb_intra)
                {
                    mblock.recon_right_for_prev = 0;
                    mblock.recon_down_for_prev = 0;
                    mblock.recon_right_back_prev = 0;
                    mblock.recon_down_back_prev = 0;
                }
                else
                {
                    /* If no forw vectors, current vectors equal prev. vectors. */
                    if (!mb_motion_forw)
                    {
                        recon_right_for = mblock.recon_right_for_prev;
                        recon_down_for = mblock.recon_down_for_prev;
                    }
                    /*
                     * Otherwise compute forw. vectors. Reset prev vectors to new values.
                     */
                    else
                        ComputeForwVector(ref recon_right_for, ref recon_down_for);
      
                    /* If no back vectors, set back vectors to prev back vectors. */
      
                    if (!mb_motion_back)
                    {
                        recon_right_back = mblock.recon_right_back_prev;
                        recon_down_back = mblock.recon_down_back_prev;
                    }
                    /* Otherwise compute new vectors and reset prev. back vectors. */

                    else
                        ComputeBackVector(ref recon_right_back, ref recon_down_back);

                    /*
                     * Store vector existence flags in structure for possible skipped
                     * macroblocks to follow.
                     */

                    mblock.bpict_past_forw = mb_motion_forw;
                    mblock.bpict_past_back = mb_motion_back;
                }
            }

            {
                MBlockInfo mbi = new MBlockInfo(ref mblock, false);

                mbi.mb_motion_forw = mb_motion_forw;
                mbi.mb_motion_back = mb_motion_back;
                _recon.Macroblocks[mblock.mb_address] = mbi;
            }

            for (int mask = 32, i = 0; i < 6; mask >>= 1, i++)
            {
                /* If block exists... */
                if ((mblock.mb_intra) || ((mblock.cbp & mask) != 0))
                {
                    zero_block_flag = 0;
                    ParseReconBlock(i);
                }
                else
                    zero_block_flag = 1;

                _recon.Blocks[mblock.mb_address * 6 + i].SetReconInfo(zero_block_flag != 0, recon_right_for, recon_down_for, recon_right_back, recon_down_back);
            }


            /* If D Type picture, flush marker bit. */
            if (picture.code_type == 4)
                flush_bits(1);

            /* If macroblock was intracoded, set macroblock past intra address. */
            if (mblock.mb_intra)
                mblock.past_intra_addr =
                    mblock.mb_address;

            return PARSE_OK;
        }

    }

}
